#include <stdarg.h>

#include "xmlrpc_config.h"

#include "bool.h"

#include <xmlrpc-c/base.h>
#include <xmlrpc-c/client.h>
#include <xmlrpc-c/client_int.h>
#include <xmlrpc-c/client_global.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>

/*=========================================================================
   Global Client
=========================================================================*/

static struct xmlrpc_client * globalClientP;
static bool globalClientExists = false;




static void
dieIfFaultOccurred (xmlrpc_env * const envP) {
    if (envP->fault_occurred) {
        fprintf(stderr, "ERROR: %s (%d)\n",
                envP->fault_string, envP->fault_code);
        pthread_exit(1);
    }
}

void 
xmlrpc_client_init2(xmlrpc_env *                      const envP,
                    int                               const flags,
                    const char *                      const appname,
                    const char *                      const appversion,
                    const struct xmlrpc_clientparms * const clientparmsP,
                    unsigned int                      const parmSize) {
/*----------------------------------------------------------------------------
   This function is not thread-safe.
-----------------------------------------------------------------------------*/
    if (globalClientExists)
        xmlrpc_faultf(
            envP,
            "Xmlrpc-c global client instance has already been created "
            "(need to call xmlrpc_client_cleanup() before you can "
            "reinitialize).");
    else {
        /* The following call is not thread-safe */
        xmlrpc_client_setup_global_const(envP);
        if (!envP->fault_occurred) {
            xmlrpc_client_create(envP, flags, appname, appversion,
                                 clientparmsP, parmSize, &globalClientP);
            if (!envP->fault_occurred)
                globalClientExists = true;

            if (envP->fault_occurred)
                xmlrpc_client_teardown_global_const();
        }
    }
}



void
xmlrpc_client_init(int          const flags,
                   const char * const appname,
                   const char * const appversion) {
/*----------------------------------------------------------------------------
   This function is not thread-safe.
-----------------------------------------------------------------------------*/
    struct xmlrpc_clientparms clientparms;

    /* As our interface does not allow for failure, we just fail silently ! */
    
    xmlrpc_env env;
    xmlrpc_env_init(&env);

    clientparms.transport = NULL;

    /* The following call is not thread-safe */
    xmlrpc_client_init2(&env, flags,
                        appname, appversion,
                        &clientparms, XMLRPC_CPSIZE(transport));

    xmlrpc_env_clean(&env);
}



void 
xmlrpc_client_cleanup() {
/*----------------------------------------------------------------------------
   This function is not thread-safe
-----------------------------------------------------------------------------*/
    XMLRPC_ASSERT(globalClientExists);

    xmlrpc_client_destroy(globalClientP);

    globalClientExists = false;

    /* The following call is not thread-safe */
    xmlrpc_client_teardown_global_const();
}



static void
validateGlobalClientExists(xmlrpc_env * const envP) {

    if (!globalClientExists)
        xmlrpc_faultf(envP,
                      "Xmlrpc-c global client instance "
                      "has not been created "
                      "(need to call xmlrpc_client_init2()).");
}



void
xmlrpc_client_transport_call(
    xmlrpc_env *               const envP,
    void *                     const reserved ATTR_UNUSED, 
        /* for client handle */
    const xmlrpc_server_info * const serverP,
    xmlrpc_mem_block *         const callXmlP,
    xmlrpc_mem_block **        const respXmlPP) {

    validateGlobalClientExists(envP);
    if (!envP->fault_occurred)
        xmlrpc_client_transport_call2(envP, globalClientP, serverP,
                                      callXmlP, respXmlPP);
}



xmlrpc_value * 
xmlrpc_client_call(xmlrpc_env * const envP,
                   const char * const serverUrl,
                   const char * const methodName,
                   const char * const format,
                   ...) {

    xmlrpc_value * resultP;

    validateGlobalClientExists(envP);

   // printf("I'm here!");

    if (!envP->fault_occurred) {
        va_list args;

        va_start(args, format);
    
        xmlrpc_client_call2f_va(envP, globalClientP, serverUrl,
                                methodName, format, &resultP, args);

        va_end(args);
    }
    return resultP;
}

typedef struct
{
	xmlrpc_env * envP;
	char *  serverUrl;
	char * methodName;
	char *  format;
	xmlrpc_value **  resultPP;
	//va_list args;
	int semantics;

}Parameters;

typedef struct
{
	int active;  //1 = active
	int req_id;
	int* responses;			//Address to integer array of responses
	int size;				//Total size of responses array...should always be 3
	int current_responses;  //
}Request;

typedef struct
{
	int active;  //1 = active
	xmlrpc_value * final_result;
	//int req_id;
	int* responses;			//Address to integer array of responses
	int size;				//Total size of responses array...should always be 3
	int current_responses;  //
}s_request;

pthread_cond_t myconvar = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t threadmutex = PTHREAD_MUTEX_INITIALIZER;
pthread_barrier_t threadbarrier;

static s_request sync_request;

//Returns -1 if no winner, 0 for heads, 1 for tails
int findSyncWinner(s_request* request)
{
	int head_count =0; //0
	int tail_count =0; //1
	int majority_count = (request->size/2)+1;
	int i;
	for(i=0;i<request->current_responses;++i)
	{
		if(request->responses[i]==0)
			++head_count;
		else if(request->responses[i]==1)
			++tail_count;

		if(head_count>=majority_count||tail_count>=majority_count)
			break;
	}

	if(head_count>=majority_count)
		return 0;
	else if(tail_count>=majority_count)
		return 1;
	else
		return -1;

}

static void
       cleanup_handler(void *arg)
       {
			pthread_mutex_unlock(&threadmutex);
			//exit(0);
			return;
       }

void sighand(int signo)
{
	pthread_mutex_unlock(&threadmutex);
	int old_value;
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_value);

				//exit(0);
	//printf("term sig caught\n");
	//pthread_exit(NULL);
}

void * sync_func(void* arg)
{
	Parameters* param = (Parameters*)arg;
	//va_start(param->args, param->format);
	xmlrpc_value * resultp;
	//xmlrpc_int result;
	xmlrpc_int32 result;
	int doImatter = 1;
	signal(SIGUSR1, sighand);
	pthread_barrier_wait(&threadbarrier);

	//printf("Response STARTED");
	//printf("Got mutex\n");
	resultp = xmlrpc_client_call(param->envP,
						param->serverUrl,
						param->methodName,
						param->format);

	int old_value;
	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_value);
	if(old_value == PTHREAD_CANCEL_DISABLE)
	{
		pthread_exit(0);//I don't matter anymore
	}
	//printf("Tyring to get mutex");
	pthread_mutex_lock(&threadmutex);
	//pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
	//printf("blocking call complete\n");

	if(sync_request.active == 0)
		{
			//printf("Error...this sync function is deactivated\n");
			pthread_mutex_unlock(&threadmutex);
			pthread_exit(NULL);

		}


	int next_response_index = sync_request.current_responses;
	sync_request.responses[next_response_index] = -1;
	++sync_request.current_responses;

	dieIfFaultOccurred(param->envP);
	xmlrpc_read_int(param->envP, resultp, &result);
	//printf("Response received...value = %d\n", result);

	//xmlrpc_client_call2f(param->envP, globalClientP, ,
	  //      	                                , &resultp, param->format);
	//*resultp->_type = XMLRPC_TYPE_INT;

	//va_end(param->args);


	xmlrpc_value * new_resultp;




		int winner = -1;
		switch(param->semantics)
		{
			case 0:  //ANY
				//
				sync_request.final_result = resultp;
				sync_request.active = 0;
				pthread_mutex_lock(&mymutex);
				//printf("Sending\n");
				pthread_cond_signal (&myconvar);
				pthread_mutex_unlock(&mymutex);
				pthread_mutex_unlock(&threadmutex);
				break;
			case 1:  //Majority

				//xmlrpc_read_int(&param->envP, resultp, &result);
				//pthread_mutex_lock(&mymutex);
				sync_request.responses[next_response_index] = result;
				//++sync_request.current_responses;
				if(sync_request.current_responses >= (sync_request.size/2)+1)
				{
					winner = findSyncWinner(&sync_request);
					if(winner!=-1)
					{
						new_resultp = xmlrpc_int_new(param->envP, winner);
						sync_request.final_result = new_resultp;
						sync_request.active = 0;
						pthread_mutex_lock(&mymutex);
						pthread_cond_signal (&myconvar);
						pthread_mutex_unlock(&mymutex);

						//We have a value
					}
				}
				pthread_mutex_unlock(&threadmutex);
				//pthread_mutex_unlock(&mymutex);

				break;
			case 2: //All
				//xmlrpc_int result;
				//xmlrpc_read_int(&param->envP, resultp, &result);
				//pthread_mutex_lock(&mymutex);
				sync_request.responses[next_response_index] = result;
				//++sync_request.current_responses;
				if(sync_request.current_responses == sync_request.size)
				{
					winner = findSyncWinner(&sync_request);
					//if(winner!=-1)
					//{
						new_resultp = xmlrpc_int_new(param->envP, winner);
						sync_request.final_result = new_resultp;
						sync_request.active = 0;
						pthread_mutex_lock(&mymutex);
						pthread_cond_signal (&myconvar);
						pthread_mutex_unlock(&mymutex);

					//}
					//else
						//printf("Error:...Do you have even number of servers?\n");
				}
				pthread_mutex_unlock(&threadmutex);
				//pthread_mutex_unlock(&mymutex);

				break;
			default:
				printf("Error, invalid semantic");
				pthread_mutex_unlock(&threadmutex);
				break;

		}

		//pthread_mutex_unlock(&threadmutex);



	//When we get here, this call is complete



}




xmlrpc_value *
multirpc_client_call(int semantics,  //0 - Any, 1 - Majority, 2 - all
					int num_servers,
					xmlrpc_env * const envP,
					const char * const serverUrl,
                   const char * const methodName,
                   const char * const format,
                   ...)
{
	//Not thread-safe
	//Call num_server threads that all call sync calls
	//Condition variable will determine if we can exit out of this function
	int i;
	pthread_t threads[11];
	va_list args;
	va_start(args, format);

	//initiliaze parameter variable needed by thread
	Parameters p[num_servers];

	//p.args = args;
	xmlrpc_value * initial = xmlrpc_int_new(envP, 5);
	//Initialize global sync_request
	//pthread_mutex_lock(&threadmutex);
	sync_request.active = 1;
	sync_request.current_responses = 0;
	sync_request.size = num_servers;
	sync_request.responses = (int*)malloc(sizeof(int)*num_servers);
	sync_request.final_result = initial;

	char my_url[40];

	pthread_barrier_init(&threadbarrier, NULL, num_servers+1);

	for(i=0;i<num_servers;++i)
	{
		sprintf(my_url, "http://localhost:808%d/RPC2", i);
		p[i].serverUrl = my_url;
		p[i].envP = envP;
		p[i].semantics = semantics;
		p[i].format = format;
		p[i].methodName = methodName;
		pthread_create(&threads[i], NULL, sync_func, (void*)&p[i]);
	}
	pthread_barrier_wait(&threadbarrier);
	//pthread_mutex_unlock(&threadmutex);
	pthread_mutex_lock(&mymutex);
	//printf("Waiting\n");
	pthread_cond_wait(&myconvar, &mymutex);
	pthread_mutex_unlock(&mymutex);
	//printf("Released\n");
	//Kill all threads
	for(i=0;i<num_servers;++i)
	{
		//printf("KillING thread %d", i);
		pthread_kill(threads[i], SIGUSR1);

		//pthread_cancel(threads[i]);
	}

	pthread_barrier_destroy(&threadbarrier);
	//for(i=0;i<num_servers;++i)
	//{
		//pthread_join(threads[i], NULL);
		//printf("Joined thread %d", i);

	//}

	return sync_request.final_result;

}

#define SIZE_OF_REQUEST_ARRAY 10


typedef struct
{
	int semantics;
	int req_id;
	xmlrpc_response_handler* callback;
}carrier;



static Request active_request[SIZE_OF_REQUEST_ARRAY];




//Returns -1 if no winner, 0 for heads, 1 for tails
int findWinner(Request* request)
{
	int head_count =0; //0
	int tail_count =0; //1
	int majority_count = (request->size/2)+1;
	int i;
	for(i=0;i<request->current_responses;++i)
	{
		if(request->responses[i]==0)
			++head_count;
		else if(request->responses[i]==1)
			++tail_count;

		if(head_count>=majority_count||tail_count>=majority_count)
			break;
	}

	if(head_count>=majority_count)
		return 0;
	else if(tail_count>=majority_count)
		return 1;
	else
		return -1;

}




static void
async_response_handler(const char *   const serverUrl,
                           const char *   const methodName,
                           xmlrpc_value * const paramArrayP,
                           void *          user_data,
                           xmlrpc_env *   const faultP,
                           xmlrpc_value * resultP)
{

    xmlrpc_env env;
    xmlrpc_int addend, adder;
    //carrier* my_carrier = (carrier*)malloc(sizeof(carrier));
    //memcpy(user_data, my_carrier, sizeof(carrier));
    carrier* my_carrier = (carrier*) user_data;
    xmlrpc_value * new_resultp;

    //int semantic = (long int)user_data;
    /* Initialize our error environment variable */
    xmlrpc_env_init(&env);

    /* Our first four arguments provide helpful context.  Let's grab the
       addends from our parameter array.
    */
    int this_req_index = (my_carrier->req_id)%SIZE_OF_REQUEST_ARRAY;
    int next_response_index = active_request[this_req_index].current_responses;
    //xmlrpc_decompose_value(&env, paramArrayP, "(ii)", &addend, &adder);
    //die_if_fault_occurred(&env);
    int winner;

    if(active_request[this_req_index ].active ==0)
            		{
            			//printf("Error: This is a stray response...request is no longer activated\n");
            			return;

            		}

    xmlrpc_int sum;
    xmlrpc_read_int(&env, resultP, &sum);

  //printf("RPC #%d with method '%s' at URL '%s' has completed with %d\n",my_carrier->req_id, methodName, serverUrl, sum);

    user_data = (void*)my_carrier->req_id;

    if (faultP->fault_occurred)
    {
    	new_resultp = xmlrpc_int_new(&env, -1);
		//resultP->_value.i = winner;
		active_request[this_req_index].active =0;
		(*my_carrier->callback)(serverUrl,
																		methodName,
																		paramArrayP,
																		NULL,
																		faultP,
																		new_resultp );

        printf("The RPC failed.  %s\n", faultP->fault_string);
    }
    else
    {
    	if(my_carrier->semantics == 0)  //ANY
    	{
    		//printf("Semantics = %d", my_carrier->semantics);
    		//printf("Doing the ANY semantic\n");
    		active_request[this_req_index].active = 0;
    		(*my_carrier->callback)(serverUrl,
                           	   	   methodName,
                           	   	   paramArrayP,
                           	   	   NULL,
                           	   	   faultP,
                           	   	   resultP);
    		//resultP->_value.i = k;
    		//xmlrpc_int sum;
	        //xmlrpc_read_int(&env, resultP, &sum);
    		//die_if_fault_occurred(&env);

    		//printf("The sum is  %d\n", sum);

    	}
    	else if(my_carrier->semantics ==1)  //Majority
    	{
    		//printf("Semantics = %d", my_carrier->semantics);
    		//printf("Doing the MAJORITY semantic\n");
    		xmlrpc_int result;
    		xmlrpc_read_int(&env, resultP, &result);
    		active_request[this_req_index].responses[next_response_index] = result;
    		++active_request[this_req_index].current_responses;

    		if(active_request[this_req_index].current_responses == (active_request[this_req_index ].size))
    			{
    				//This is last response

    				winner = findWinner(&active_request[this_req_index]);
					//if(winner!=-1)
					//{
						new_resultp = xmlrpc_int_new(&env, winner);
					//}
					//else
						//printf("Was there an even number of servers, couldn't find winner\n");

					active_request[this_req_index].active = 0;
    				(*my_carrier->callback)(serverUrl,
    				    		            methodName,
    				    		            paramArrayP,
    				    		            NULL,
    				    		            faultP,
    				    		            new_resultp );


    			}
    			else if(active_request[this_req_index].current_responses >= ((active_request[this_req_index ].size)/2)+1 )
    			{
    				winner = findWinner(&active_request[this_req_index]);
    				if(winner!=-1)
    				{
    					new_resultp = xmlrpc_int_new(&env, winner);
    					//resultP->_value.i = winner;
    					active_request[this_req_index].active =0;
    					(*my_carrier->callback)(serverUrl,
    					    				    				    		            methodName,
    					    				    				    		            paramArrayP,
    					    				    				    		            NULL,
    					    				    				    		            faultP,
    					    				    				    		            new_resultp );
    				}
    				//else
    					//No winners yet, get next response
    			}
    	}
    	else if(my_carrier->semantics ==2)  //All
    	{
    		//printf("Semantics = %d", my_carrier->semantics);
    		//printf("Doing the ALL semantic\n");

    		    		xmlrpc_int result;
    		    		xmlrpc_read_int(&env, resultP, &result);
    		    		active_request[this_req_index].responses[next_response_index] = result;
    		    		++active_request[this_req_index].current_responses;

    		    		if(active_request[this_req_index].current_responses == (active_request[this_req_index ].size))
    		    			{
    		    				//This is last response

    		    				winner = findWinner(&active_request[this_req_index]);
    							//if(winner!=-1)
    							//{
    								new_resultp = xmlrpc_int_new(&env,
    								               (xmlrpc_int32)winner);
    								//resultP->_value.i = winner;
    							//}
    							//else
    								//printf("Was there an even number of servers, couldn't find winner\n");
    							active_request[this_req_index].active = 0;
    		    				(*my_carrier->callback)(serverUrl,
    		    				    		            methodName,
    		    				    		            paramArrayP,
    		    				    		            NULL,
    		    				    		            faultP,
    		    				    		            new_resultp);


    		    			}
    	}



        //xmlrpc_int sum;

        //xmlrpc_read_int(&env, resultP, &sum);
        //die_if_fault_occurred(&env);

        //printf("The sum is  %d\n", sum);



    }

}




void
multirpc_client_async_call(const int semantics,  //0 - Any, 1 - Majority, 2 - all
						int num_servers,		//Num_servers
						const char * const serverUrl,  //Not really using this starting http://localhost:8080/RPC2 increasing by num_servers
						const char * const methodName,
						xmlrpc_response_handler responseHandler,
					    void *       const userData,  //request_num
					    const char * const format,
					    ...)
{

    xmlrpc_env env[num_servers];
    char my_url[40];
    //strcpy(my_url,serverUrl);
    int i;
    int req_id = (int)userData;
    carrier* new_userData = (carrier*)malloc(sizeof(carrier));
    new_userData->semantics = semantics;

    new_userData->callback = responseHandler;
    new_userData->req_id = req_id;

    if( active_request[(req_id)%SIZE_OF_REQUEST_ARRAY].active !=1)
    {
    	active_request[(req_id)%SIZE_OF_REQUEST_ARRAY].active = 1;
    	active_request[(req_id)%SIZE_OF_REQUEST_ARRAY].req_id = req_id;
    	active_request[(req_id)%SIZE_OF_REQUEST_ARRAY].responses = (int*)malloc(sizeof(int)*num_servers);
    	active_request[(req_id)%SIZE_OF_REQUEST_ARRAY].current_responses = 0;
    	active_request[(req_id)%SIZE_OF_REQUEST_ARRAY].size = num_servers;
    }
    else
    {
    	printf("Error...need larger buffer for responses");
    }

   for(i=0;i<num_servers;++i)
    {

    	xmlrpc_env_init(&env[i]);
    	//printf("Here2\n");
    	validateGlobalClientExists(&env[i]);


		if (!env[i].fault_occurred)
		{
			va_list args;

			va_start(args, format);
			sprintf(my_url, "http://localhost:808%d/RPC2", i);


			xmlrpc_client_start_rpcf_va(&env[i], globalClientP,
                                    my_url, methodName,
                                    async_response_handler, (void*)new_userData,
                                    format, args);


			va_end(args);
		}
		if (env[i].fault_occurred)
			(*responseHandler)(my_url, methodName, NULL, userData, &env, NULL);

		xmlrpc_env_clean(&env[i]);

    }



}






xmlrpc_value * 
xmlrpc_client_call_server(xmlrpc_env *               const envP,
                          const xmlrpc_server_info * const serverInfoP,
                          const char *               const methodName,
                          const char *               const format, 
                          ...) {

    xmlrpc_value * resultP;

    validateGlobalClientExists(envP);

    if (!envP->fault_occurred) {
        va_list args;

        va_start(args, format);

        xmlrpc_client_call_server2_va(envP, globalClientP, serverInfoP,
                                      methodName, format, args, &resultP);
        va_end(args);
    }
    return resultP;
}



xmlrpc_value *
xmlrpc_client_call_server_params(
    xmlrpc_env *               const envP,
    const xmlrpc_server_info * const serverInfoP,
    const char *               const methodName,
    xmlrpc_value *             const paramArrayP) {

    xmlrpc_value * resultP;

    validateGlobalClientExists(envP);

    if (!envP->fault_occurred)
        xmlrpc_client_call2(envP, globalClientP,
                            serverInfoP, methodName, paramArrayP,
                            &resultP);

    return resultP;
}



xmlrpc_value * 
xmlrpc_client_call_params(xmlrpc_env *   const envP,
                          const char *   const serverUrl,
                          const char *   const methodName,
                          xmlrpc_value * const paramArrayP) {

    xmlrpc_value * resultP;

    validateGlobalClientExists(envP);

    if (!envP->fault_occurred) {
        xmlrpc_server_info * serverInfoP;

        serverInfoP = xmlrpc_server_info_new(envP, serverUrl);
        
        if (!envP->fault_occurred) {
            xmlrpc_client_call2(envP, globalClientP,
                                serverInfoP, methodName, paramArrayP,
                                &resultP);
            
            xmlrpc_server_info_free(serverInfoP);
        }
    }
    return resultP;
}                            



void 
xmlrpc_client_call_server_asynch_params(
    xmlrpc_server_info * const serverInfoP,
    const char *         const methodName,
    xmlrpc_response_handler    responseHandler,
    void *               const userData,
    xmlrpc_value *       const paramArrayP) {

    xmlrpc_env env;

    xmlrpc_env_init(&env);

    validateGlobalClientExists(&env);

    if (!env.fault_occurred)
        xmlrpc_client_start_rpc(&env, globalClientP,
                                serverInfoP, methodName, paramArrayP,
                                responseHandler, userData);

    if (env.fault_occurred) {
        /* Unfortunately, we have no way to return an error and the
           regular callback for a failed RPC is designed to have the
           parameter array passed to it.  This was probably an oversight
           of the original asynch design, but now we have to be as
           backward compatible as possible, so we do this:
        */
        (*responseHandler)(serverInfoP->serverUrl,
                           methodName, paramArrayP, userData,
                           &env, NULL);
    }
    xmlrpc_env_clean(&env);
}



void 
xmlrpc_client_call_asynch(const char * const serverUrl,
                          const char * const methodName,
                          xmlrpc_response_handler responseHandler,
                          void *       const userData,
                          const char * const format,
                          ...) {

    xmlrpc_env env;

    xmlrpc_env_init(&env);
    printf("Here2\n");
    validateGlobalClientExists(&env);

    if (!env.fault_occurred) {
        va_list args;

        va_start(args, format);

        xmlrpc_client_start_rpcf_va(&env, globalClientP,
                                    serverUrl, methodName,
                                    responseHandler, userData,
                                    format, args);

        va_end(args);
    }
    if (env.fault_occurred)
        (*responseHandler)(serverUrl, methodName, NULL, userData, &env, NULL);

    xmlrpc_env_clean(&env);
}



void
xmlrpc_client_call_asynch_params(const char *   const serverUrl,
                                 const char *   const methodName,
                                 xmlrpc_response_handler responseHandler,
                                 void *         const userData,
                                 xmlrpc_value * const paramArrayP) {
    xmlrpc_env env;
    xmlrpc_server_info * serverInfoP;

    xmlrpc_env_init(&env);

    serverInfoP = xmlrpc_server_info_new(&env, serverUrl);

    if (!env.fault_occurred) {
        xmlrpc_client_call_server_asynch_params(
            serverInfoP, methodName, responseHandler, userData, paramArrayP);
        
        xmlrpc_server_info_free(serverInfoP);
    }
    if (env.fault_occurred)
        (*responseHandler)(serverUrl, methodName, paramArrayP, userData,
                           &env, NULL);
    xmlrpc_env_clean(&env);
}



void 
xmlrpc_client_call_server_asynch(xmlrpc_server_info * const serverInfoP,
                                 const char *         const methodName,
                                 xmlrpc_response_handler    responseHandler,
                                 void *               const userData,
                                 const char *         const format,
                                 ...) {

    xmlrpc_env env;

    validateGlobalClientExists(&env);

    if (!env.fault_occurred) {
        va_list args;
    
        xmlrpc_env_init(&env);

        va_start(args, format);

        xmlrpc_client_start_rpcf_server_va(
            &env, globalClientP, serverInfoP, methodName,
            responseHandler, userData, format, args);

        va_end(args);
    }
    if (env.fault_occurred)
        (*responseHandler)(serverInfoP->serverUrl, methodName, NULL,
                           userData, &env, NULL);

    xmlrpc_env_clean(&env);
}



void 
xmlrpc_client_event_loop_finish_asynch(void) {

    XMLRPC_ASSERT(globalClientExists);
    xmlrpc_client_event_loop_finish(globalClientP);
}



void 
xmlrpc_client_event_loop_finish_asynch_timeout(
    unsigned long const milliseconds) {

    XMLRPC_ASSERT(globalClientExists);
    xmlrpc_client_event_loop_finish_timeout(globalClientP, milliseconds);
}
